package com.iuie.ns.system.service.report.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.iuie.ns.frame.exception.ServiceException;
import com.iuie.ns.frame.utils.StringTemplateUtils;
import com.iuie.ns.frame.utils.StringUtils;
import com.iuie.ns.frame.web.dataset.vo.Page;
import com.iuie.ns.system.mapper.report.ReportInfoMapper;
import com.iuie.ns.system.service.code.ISysCodeInfoService;
import com.iuie.ns.system.service.report.IReportInfoService;
import com.iuie.ns.system.service.report.IReportParamsService;
import com.iuie.ns.system.vo.code.SysCodeInfoVO;
import com.iuie.ns.system.vo.report.ReportInfoVO;

@Service("reportService")
public class ReportInfoServiceImpl implements IReportInfoService {
	
	@Resource
	private ReportInfoMapper reportInfoMapper;
	
	@Resource
	private IReportParamsService reportParamsService;
	
	@Resource
	private ISysCodeInfoService codeInfoService;
	
	private static final Logger logger = LoggerFactory.getLogger(ReportInfoServiceImpl.class);

	@Override
	public ReportInfoVO findByReportKey(String reportCategory, String reportKey) {
		if (StringUtils.isBlank(reportCategory)) {
			throw new ServiceException("报表所属业务不能为空！");
		}
		if (StringUtils.isBlank(reportKey)) {
			throw new ServiceException("报表KEY不能为空！");
		}
		return reportInfoMapper.findByReportKey(reportCategory, reportKey);
	}

	@Override
	public List<ReportInfoVO> findByPage(Page page, ReportInfoVO condition) {
		PageHelper.startPage(page.getPage(), page.getRows(), true);
		List<ReportInfoVO> list = reportInfoMapper.findByCondition(condition);
		page.loadData(list);
		return list;
	}
	
	private void validate(ReportInfoVO item) {
		if (item == null) {
			logger.info("保存报表信息失败，对象不能为空！");
			throw new ServiceException("保存报表信息失败，对象不能为空！");
		}
		if (StringUtils.isBlank(item.getReportKey())) {
			throw new ServiceException("保存报表信息失败，报表KEY不能为空！");
		}
		if (StringUtils.isBlank(item.getReportName())) {
			throw new ServiceException("保存报表信息失败，报表名称不能为空！");
		}
		if (StringUtils.isBlank(item.getReportJson())) {
			throw new ServiceException("保存报表信息失败，报表JSON对象不能为空！");
		}
		if (StringUtils.isBlank(item.getReportCategory())) {
			throw new ServiceException("保存报表信息失败，报表所属业务不能为空！");
		}
	}

	@Override
	public void addOne(ReportInfoVO item) {
		this.validate(item);
		if (StringUtils.isBlank(item.getReportId())) {
			// 新增
			ReportInfoVO ri = this.findByReportKey(item.getReportCategory(), item.getReportKey());
			if (ri != null) {
				// 数据库中已存在
				SysCodeInfoVO reportCategory = codeInfoService.findCodeInfoByCodeValueAndCodeType(item.getReportCategory(), "reportCategory");
				throw new ServiceException("新增失败，系统中已存在报表KEY为" + item.getReportKey() + ", 所属业务为【" + reportCategory.getCodeInfoName() + "】");
			}
			item.setReportId(StringUtils.uuid());
			item.setCreateTime(new Date());
			reportInfoMapper.addOne(item);
		} else {
			// 修改
			item.setUpdateTime(new Date());
			reportInfoMapper.updateOne(item);
		}
	}

	@Override
	public Integer deleteById(String reportId) {
		if (StringUtils.isBlank(reportId)) {
			throw new ServiceException("报表id不能为空！");
		}
		reportParamsService.deleteByReportId(reportId); // 删除该报表的所有参数
		Integer num = reportInfoMapper.deleteById(reportId);
		return num;
	}
	
	@Override
	public ReportInfoVO findById(String reportId) {
		if (StringUtils.isBlank(reportId)) {
			throw new ServiceException("报表id不能为空！");
		}
		return reportInfoMapper.findById(reportId);
	}
	
	@Override
	public Map<String, String> findReportJson(String reportCategory, String reportKey, HttpServletRequest request) {
		ReportInfoVO info = this.findByReportKey(reportCategory, reportKey);
		if (info == null) {
			throw new ServiceException("没有查询到报表信息！");
		}
		String tempalte = info.getReportJson();
		Map<String, String> params = reportParamsService.findMapOfParams(info.getReportId());
		
		String dataLoader = info.getDataLoader();
		if (!StringUtils.isBlank(dataLoader)) {
			// 通过dataLoader去加载数据
			loadData(info, dataLoader, params, request);
		}
		
		String json = StringTemplateUtils.render(tempalte, params, "\\{\\{(.+?)\\}\\}");
		
		Map<String, String> map = new HashMap<>();
		map.put("reportKey", reportKey);
		map.put("json", json);
		return map;
	}

	private void loadData(ReportInfoVO info, String dataLoader, Map<String, String> params, HttpServletRequest request) {
		String[] arr = StringUtils.split(dataLoader, ":");
		if (arr == null || arr.length != 3) {
			throw new ServiceException("报表【" + info.getReportKey() + "】的数据加载器【" + dataLoader + "】非法！");
		}
		String className = arr[0];
		String serviceName = arr[1];
		String methodName = arr[2];
		
		Class<?> clazz = null;
		try {
			clazz = Class.forName(className);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			throw new ServiceException("报表【" + info.getReportKey() + "】的数据加载器【" + dataLoader + "】，找不到类【" + className + "】！");
		}
		
		// 从servlet容器中获取对象
		WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
		Object serivce = context.getBean(serviceName);
		
		if (serivce == null) {
			throw new ServiceException("报表【" + info.getReportKey() + "】的数据加载器【" + dataLoader + "】，找不到指定对象【" + serviceName + "】！");
		}
		
		// 执行指定类对象的方法
		Map<String, Object> map = new HashMap<>();
		try {
			Method method = clazz.getDeclaredMethod(methodName, Map.class, HttpServletRequest.class);
			method.setAccessible(true);
			method.invoke(serivce, map, request);
		} catch (Exception e) {
			e.printStackTrace();
			// 如果是ServiceException，直接抛出
			if (e instanceof InvocationTargetException) {
				InvocationTargetException ite = (InvocationTargetException)e;
				if (ite.getTargetException() instanceof ServiceException) {
					throw (ServiceException)ite.getTargetException();
				}
			}
			throw new ServiceException("报表【" + info.getReportKey() + "】的数据加载器【" + dataLoader + "】，方法执行失败【" + methodName + "】！");
		}
		
		// 将map的值对象转成String，put入params
		try {
			Iterator<Entry<String, Object>> it = map.entrySet().iterator();
			while (it.hasNext()) {
				Entry<String, Object> en = it.next();
				ObjectMapper mapper = new ObjectMapper();
				mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
				String json = mapper.writeValueAsString(en.getValue());
				params.put(en.getKey(), json);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServiceException("报表【" + info.getReportKey() + "】的数据加载器【" + dataLoader + "】，map的值对象转字符串失败！");
		}
	}
}
